package com.cw.jeeyt.service.impl.system;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import com.cw.jeeyt.common.enums.EnumDataJavaType;
import com.cw.jeeyt.common.enums.EnumGenerateCodeType;
import com.cw.jeeyt.common.enums.EnumMysqlDataType;
import com.cw.jeeyt.common.enums.EnumYn;
import com.cw.jeeyt.common.utils.FileUtils;
import com.cw.jeeyt.common.utils.StringUtil;
import com.cw.jeeyt.service.bo.security.SecurityUserBo;
import com.cw.jeeyt.service.bo.system.SystemColumnInfoBo;
import com.cw.jeeyt.service.bo.system.SystemDictCacheBo;
import com.cw.jeeyt.service.bo.system.SystemModuleInfoBo;
import com.cw.jeeyt.service.bo.system.SystemPkInfoBo;
import com.cw.jeeyt.service.bo.system.SystemProjectInfoBo;
import com.cw.jeeyt.service.bo.system.SystemTableInfoBo;
import com.cw.jeeyt.service.bo.system.SystemTemplateInfoBo;
import com.cw.jeeyt.service.bo.system.SystemTemplateModuleInfoBo;
import com.cw.jeeyt.core.util.SessionUtil;
import com.cw.jeeyt.service.intf.system.SystemAutoCodeService;
import com.cw.jeeyt.service.intf.system.SystemColumnInfoService;
import com.cw.jeeyt.service.intf.system.SystemModuleInfoService;
import com.cw.jeeyt.service.intf.system.SystemProjectInfoService;
import com.cw.jeeyt.service.intf.system.SystemTableInfoService;
import com.cw.jeeyt.service.intf.system.SystemTemplateInfoService;
import com.cw.jeeyt.service.intf.system.SystemTemplateModuleInfoService;
import com.cw.lang.common.utils.CollectionUtil;
import com.cw.lang.common.utils.SequenceTool;
import com.cw.frame.codegen.Configuration;
import com.cw.frame.codegen.JavaFileGenerator;
import com.cw.frame.codegen.PageFileGenerator;
import com.cw.frame.codegen.XmlFileGenerator;
import com.cw.frame.codegen.engine.TemplateEngine;
import com.cw.frame.codegen.engine.VelocityTemplateEngine;
import com.cw.frame.codegen.parameter.DatabaseSchemaReader;
import com.cw.frame.codegen.parameter.EnumField;
import com.cw.frame.codegen.parameter.EnumItemField;
import com.cw.frame.codegen.parameter.Field;
import com.cw.frame.codegen.parameter.ParamContext;
import com.cw.frame.codegen.parameter.ParamContextFactory;
import com.cw.frame.codegen.parameter.ParamContextFactory4EntityClassImpl;
import com.cw.frame.codegen.parameter.ParamContextFactory4JdbcImpl;
import com.cw.frame.codegen.parameter.ParamContextFactory4PageImpl;
import com.cw.frame.codegen.parameter.ParamContextFactory4PropertiesImpl;
import com.cw.frame.codegen.parameter.PkField;

import cn.hutool.core.util.ZipUtil;

/**
 * 自动生成代码业务实现类
 *
 * @author chenw
 * @version 1.0.0
 */
@Service
public class SystemAutoCodeServiceImpl implements SystemAutoCodeService {

    private static final Logger logger = LoggerFactory.getLogger(SystemAutoCodeServiceImpl.class);
    @Autowired
    private SystemProjectInfoService systemProjectInfoService;
    @Autowired
    private SystemModuleInfoService systemModuleInfoService;
    @Autowired
    @Lazy
    private SystemTableInfoService systemTableInfoService;
    @Autowired
    @Lazy
    private SystemColumnInfoService systemColumnInfoService;
    @Autowired
    private SystemTemplateInfoService systemTemplateInfoService;
    @Autowired
    private SystemTemplateModuleInfoService systemTemplateModuleInfoService;
    private List<List<Field>> fieldLists = new LinkedList<>();
    private List<List<Field>> queryLists = new LinkedList<>();
    private List<List<Field>> orderLists = new LinkedList<>();
    private List<List<Field>> listLists = new LinkedList<>();
    private List<List<Field>> formLists = new LinkedList<>();
    private List<List<PkField>> pkLists = new LinkedList<>();
    private List<EnumField> enumLists = new LinkedList<>();
    private String generateCode = "";
    private String projectAbsolutePath = "";

    /**
     * 自动生成表代码
     *
     * @param tableIds 表id集合
     * @param moduleId 模块id
     * @param generateType 生成代码方式
     */
    @Override
    public void generateCode(String tableIds, String moduleId, String generateType, HttpServletRequest request,
        HttpServletResponse response) throws Exception {
        //初始化
        // 表模型字段信息
        fieldLists = new LinkedList<>();
        // 查询字段信息
        queryLists = new LinkedList<>();
        // 排序字段信息
        orderLists = new LinkedList<>();
        // 列表字段信息
        listLists = new LinkedList<>();
        // 表单字段信息
        formLists = new LinkedList<>();
        // 表主键信息
        pkLists = new LinkedList<>();
        // 枚举信息
        enumLists = new LinkedList<>();
        Configuration configuration;
        if (EnumGenerateCodeType.PAGE.getCode()
            .equals(generateType)) {
            configuration = Configuration.getPageConfig();
        } else if (EnumGenerateCodeType.JDBC.getCode()
            .equals(generateType)) {
            configuration = Configuration.getJdbcConfig();
        } else if (EnumGenerateCodeType.CLASS.getCode()
            .equals(generateType)) {
            configuration = Configuration.getClassConfig();
        } else if (EnumGenerateCodeType.PROPERTIES.getCode()
            .equals(generateType)) {
            configuration = Configuration.getDefault();
        } else {
            throw new Exception("不能识别当前生成代码方式");
        }
        // 初始化Configuration信息
        initConfiguration(configuration, moduleId, tableIds, generateType);
        ParamContext paramContext = getParamContextFactory(generateType).create(generateType);
        if (paramContext == null) {
            System.err.println("Error: can't create param context!");
            logger.error("读取自动生成代码配置错误！");
            return;
        }

        // 将生成代码方式添加到参数上下文paramContext中
        String templateId = configuration.getTemplateId();
        List<SystemTemplateModuleInfoBo> stmibList = systemTemplateModuleInfoService.queryListByTemplateId(templateId);
        Map<String, Object> attributes = new HashMap<>();
        attributes.put("generateType", generateType);
        if (stmibList != null && !stmibList.isEmpty()) {
            attributes.put("systemTemplateModuleInfoList", stmibList);
        }
        paramContext.setAttributes(attributes);
        // 获取模板引擎
        TemplateEngine templateEngine = getTemplateEngine();
        // 生成java代码
        JavaFileGenerator javaFileGenerator = new JavaFileGenerator();
        javaFileGenerator.setTemplateEngine(templateEngine);

        javaFileGenerator.generate(paramContext);
        // 生成页面代码
        PageFileGenerator pageFileGenerator = new PageFileGenerator();
        pageFileGenerator.setTemplateEngine(templateEngine);
        pageFileGenerator.generate(paramContext);

        //生成配置文件代码
        XmlFileGenerator xmlFileGenerator = new XmlFileGenerator();
        xmlFileGenerator.setTemplateEngine(templateEngine);
        xmlFileGenerator.generate(paramContext);

        //代码生成以后下载生成的代码
        downloadSourceFile(request, response);
    }

    /**
     * 下载生成的代码
     *
     * @throws Exception
     */
    private void downloadSourceFile(HttpServletRequest request, HttpServletResponse response) throws Exception {
        String outputPath = projectAbsolutePath + File.separator + generateCode;
        File file = ZipUtil.zip(outputPath);
        FileUtils.downFile(file, request, response);
        FileUtils.deleteDirectory(outputPath);
    }

    /**
     * 初始化页面生成代码配置信息
     *
     * @param configuration 配置信息
     * @param moduleId 模块id
     * @param tableIds 表格id集合
     * @param generateType 生成代码方式
     */
    private void initConfiguration(Configuration configuration, String moduleId, String tableIds, String generateType)
    throws Exception {
        SecurityUserBo securityUserBo = SessionUtil.getSessionUser();
        // 获取工程、模块信息
        SystemModuleInfoBo systemModuleInfo = systemModuleInfoService.getSystemModuleInfoById(moduleId);
        String projectId = systemModuleInfo.getProjectId();
        String moduleCode = systemModuleInfo.getModuleCode();
        String moduleName = systemModuleInfo.getModuleName();
        SystemProjectInfoBo systemProjectInfo = systemProjectInfoService.getSystemProjectInfoById(projectId);
        // 获取模板信息
        SystemTemplateInfoBo systemTemplateInfo = systemTemplateInfoService.getSystemTemplateInfoById(
            systemProjectInfo.getTemplateId());
        if (systemTemplateInfo == null) {
            logger.info("生成代码模板信息为空");
            throw new Exception("生成代码模板信息为空");
        }
        // 配置模板路径、模块id、模板id
        configuration.setTemplateId(systemProjectInfo.getTemplateId());
        configuration.setModuleId(moduleCode);
        configuration.setModuleIdCn(moduleName);
        configuration.setProjectCode(systemProjectInfo.getProjectCode());
        configuration.setProjectType(systemProjectInfo.getProjectType());
        //模板路径
        configuration.setJavaTemplatePath(systemTemplateInfo.getTemplateJavaPath());
        configuration.setPageTemplatePath(systemTemplateInfo.getTemplatePagePath());
        configuration.setXmlTemplatePath(systemTemplateInfo.getTemplateXmlPath());
        // 设置java文件、页面、xml文件输出路径
        generateCode = SequenceTool.nextId();
        projectAbsolutePath = systemProjectInfo.getProjectAbsolutePath();
        configuration.setJavaOutputPath(projectAbsolutePath + File.separator + generateCode);
        configuration.setPageOutputPath(projectAbsolutePath + File.separator + generateCode);
        configuration.setXmlOutputPath(projectAbsolutePath + File.separator + generateCode);
        // 配置数据库连接信息
        configuration.setDriverClass(systemProjectInfo.getJdbcDriverClass());
        configuration.setJdbcUrl(systemProjectInfo.getJdbcUrl());
        configuration.setDbUserName(systemProjectInfo.getJdbcUserName());
        configuration.setDbPassword(systemProjectInfo.getJdbcUserPassword());

        // 配置表模型信息
        String[] tableIdList = tableIds.split(",");
        StringBuilder packages = new StringBuilder();
        StringBuilder classNames = new StringBuilder();
        StringBuilder tableCodes = new StringBuilder();
        StringBuilder tableCnNames = new StringBuilder();
        String packagePath = systemModuleInfo.getBasePackagePath();
        int i = 0;
        for (String tableId : tableIdList) {
            // 获取表信息
            SystemTableInfoBo systemTableInfo = systemTableInfoService.getSystemTableInfoById(tableId);
            String tableEntityCode = systemTableInfo.getTableEntityCode();
            String tableEntityCodeMapping = systemTableInfo.getTableEntityCodeMapping();
            //如果实体表编码映射不为空，则实体表编码取实体表编码映射字段
            if (StringUtil.isNotEmpty(tableEntityCodeMapping)) {
                tableEntityCode = tableEntityCodeMapping;
            }
            String tableCnName = systemTableInfo.getTableName();
            String tableCode = systemTableInfo.getTableCode();
            if (StringUtil.isNotEmpty(tableCnName)) {
                if ("表".equals(tableCnName.substring(tableCnName.length() - 1))) {
                    tableCnName = tableCnName.substring(0, tableCnName.length() - 1);
                }
            }
            List<PkField> pkList = new LinkedList<>();
            List<SystemPkInfoBo> systemPkInfoList = systemTableInfo.getPkInfoList();
            if (systemPkInfoList != null && !systemPkInfoList.isEmpty()) {
                for (SystemPkInfoBo systemPkInfo : systemPkInfoList) {
                    PkField pkFiled = new PkField();
                    BeanUtils.copyProperties(systemPkInfo, pkFiled);
                    pkList.add(pkFiled);
                }
            }
            pkLists.add(pkList);
            addDictInfo(systemTableInfo.getDictList(), packagePath);
            // 添加表对应的包路径
            if (i == 0) {
                packages.append(packagePath);
                classNames.append(tableEntityCode);
                tableCodes.append(tableCode);
                tableCnNames.append(tableCnName);
            } else {
                packages.append("," + packagePath);
                classNames.append("," + tableEntityCode);
                tableCodes.append("," + tableCode);
                tableCnNames.append("," + tableCnName);
            }
            // 配置页面表字段信息
            if (EnumGenerateCodeType.PAGE.getCode()
                .equals(generateType)) {
                configurationPageColumnInfo(tableId);
            } else if (EnumGenerateCodeType.JDBC.getCode()
                .equals(generateType)) {
                configurationJdbcColumnInfo(tableId);
            } else if (EnumGenerateCodeType.CLASS.getCode()
                .equals(generateType)) {
                configurationClassColumnInfo(tableId);
            } else if (EnumGenerateCodeType.PROPERTIES.getCode()
                .equals(generateType)) {
                configurationJdbcColumnInfo(tableId);
            }
            i++;
        }
        //添加枚举配置
        configuration.setPkList(pkLists);
        configuration.setEnumLists(enumLists);
        configuration.setFieldList(fieldLists);
        configuration.setQueryList(queryLists);
        configuration.setListList(listLists);
        configuration.setFormList(formLists);
        configuration.setOrderList(orderLists);
        configuration.setTableCnNames(tableCnNames.toString());
        configuration.setTableNames(tableCodes.toString());
        configuration.setPackages(packages.toString());
        configuration.setEntityClasses(classNames.toString());
        configuration.setAuthor(securityUserBo.getAccount());
    }

    /**
     * 配置表字段页面信息
     *
     * @param tableId
     */
    private void configurationPageColumnInfo(String tableId) throws Exception {

        List<SystemColumnInfoBo> systemColumnInfoList = systemColumnInfoService.queryColumnInfoListByTableId(tableId);
        if (systemColumnInfoList != null && !systemColumnInfoList.isEmpty()) {
            List<Field> fieldList = new LinkedList<>();
            List<Field> queryList = new LinkedList<>();
            List<Field> orderList = new LinkedList<>();
            List<Field> listList = new LinkedList<>();
            List<Field> formList = new LinkedList<>();
            for (SystemColumnInfoBo systemColumnInfo : systemColumnInfoList) {
                Field field = new Field();
                BeanUtils.copyProperties(systemColumnInfo, field);
                field.setIsPk(systemColumnInfo.getIsPk());
                field.setFieldName(systemColumnInfo.getColumnCode());
                field.setFieldCnName(systemColumnInfo.getColumnName());
                field.setFieldLength(systemColumnInfo.getColumnLength());
                field.setFieldPoint(systemColumnInfo.getColumnPoint());
                String columnCode = systemColumnInfo.getColumnCode();
                field.setFieldNameUp(columnCode.replaceFirst(columnCode.substring(0, 1), columnCode.substring(0, 1)
                    .toUpperCase()));
                field.setFieldType(EnumDataJavaType.find(systemColumnInfo.getColumnType())
                    .getValue());
                field.setColumnName(systemColumnInfo.getColumnSqlCode());
                field.setColumnType(EnumMysqlDataType.find(systemColumnInfo.getColumnSqlType())
                    .getValue());
                fieldList.add(field);
                // 判断是否列表字段
                if (EnumYn.Y.getCode()
                    .equals(systemColumnInfo.getIsListColumn())) {
                    listList.add(field);
                }
                // 判断是否form字段
                if (EnumYn.Y.getCode()
                    .equals(systemColumnInfo.getIsFormColumn())) {
                    formList.add(field);
                }
                // 判断是否排序字段
                if (EnumYn.Y.getCode()
                    .equals(systemColumnInfo.getIsOrderColumn())) {
                    orderList.add(field);
                }
                // 判断是否查询字段
                if (EnumYn.Y.getCode()
                    .equals(systemColumnInfo.getIsQueryColumn())) {
                    queryList.add(field);
                }
            }
            fieldLists.add(fieldList);
            queryLists.add(queryList);
            listLists.add(listList);
            formLists.add(formList);
            orderLists.add(orderList);
        }
    }

    /**
     * 添加枚举信息
     *
     * @param dictList
     */
    private void addDictInfo(List<SystemDictCacheBo> dictList, String packagePath) throws Exception {
        if (CollectionUtil.isEmpty(dictList)) {
            return;
        }
        for (SystemDictCacheBo dictBo : dictList) {
            if (dictBo == null) {
                return;
            }
            if (CollectionUtil.isNotEmpty(enumLists)) {
                EnumField enumField = enumLists.stream()
                    .filter(input -> dictBo.getItemCode()
                        .equals(input.getEnumCode()))
                    .findFirst()
                    .orElse(null);
                if (enumField != null) {
                    continue;
                }
            }
            //添加枚举
            EnumField enumField = new EnumField();
            enumField.setEnumCode(dictBo.getItemCode());
            enumField.setEnumName(dictBo.getItemName());
            enumField.setPackagePath(packagePath);
            List<SystemDictCacheBo> childList = dictBo.getChildList();
            List<EnumItemField> itemList = new ArrayList<>();
            if (CollectionUtil.isNotEmpty(childList)) {
                childList.forEach(child -> {
                    EnumItemField enumItemField = new EnumItemField();
                    enumItemField.setItemCode(child.getItemName());
                    enumItemField.setItemValue(child.getItemValue());
                    enumItemField.setItemDesc(child.getDictComment());
                    itemList.add(enumItemField);
                });
                enumField.setItemList(itemList);
            }
            enumLists.add(enumField);
        }
    }

    /**
     * 初始化JDBC生成代码配置信息
     */
    private void configurationJdbcColumnInfo(String tableIds) throws Exception {

        // 通过读取数据库字段信息封装
        DatabaseSchemaReader databaseSchemaReader = new DatabaseSchemaReader();
        String[] tableIdList = tableIds.split(",");
        for (String tableId : tableIdList) {
            List<Field> fieldList = databaseSchemaReader.parseTableForMysql(tableId);
            fieldLists.add(fieldList);
        }
    }

    /**
     * 初始化实体类生成代码配置信息
     */
    private void configurationClassColumnInfo(String tableIds) throws Exception {

    }

    /**
     * 根据生成代码方式获取生成代码工厂实现类
     *
     * @param generateType
     * @return
     */
    private ParamContextFactory getParamContextFactory(String generateType) throws Exception {

        if (EnumGenerateCodeType.PAGE.getCode()
            .equals(generateType)) {
            return new ParamContextFactory4PageImpl();
        } else if (EnumGenerateCodeType.JDBC.getCode()
            .equals(generateType)) {
            return new ParamContextFactory4JdbcImpl();
        } else if (EnumGenerateCodeType.CLASS.getCode()
            .equals(generateType)) {
            return new ParamContextFactory4EntityClassImpl();
        } else if (EnumGenerateCodeType.PROPERTIES.getCode()
            .equals(generateType)) {
            return new ParamContextFactory4PropertiesImpl();
        } else {
            throw new Exception("不能识别当前生成代码方式");
        }
    }

    private TemplateEngine getTemplateEngine() {
        return new VelocityTemplateEngine();
    }

    public static void main(String[] args) {
        FileUtils.writeToFile("D:\\tmp\\a.txt","package ${params.packageName}.core.bo;\n" + "\n"
            + "import java.io.Serializable;\n" + "import lombok.Data;\n" + " /**\n" + " * ${params.tableCnName}实体类\n"
            + " *\n" + " * @author ${params.author}\n" + " * @date ${params.createDate}\n" + " */\n" + "@Data\n"
            + "public class ${params.className}Bo implements Serializable {\n"
            + "\tprivate static final long serialVersionUID = ${params.serialId};\n" + "\n"
            + "\t#foreach ($fld in $params.fieldList)\n" + "\t#if($fld.isDict =='Y')\n"
            + "\t\t/** $fld.fieldCnName */\n" + "\t\tprivate $fld.fieldType $fld.fieldName;\n"
            + "\t\t/** $fld.fieldCnName */\n" + "\t\tprivate $fld.fieldType ${fld.fieldName}Cn;\n" + "\t#else\n"
            + "\t\t/** $fld.fieldCnName */\n" + "\t\tprivate $fld.fieldType $fld.fieldName;\n" + "\t#end\n" + "\t#end\n"
            + "}","utf-8");
    }
}
